#include "kernel.h"
namespace simu_kernel
{

statement state = {1, 1, 14};

struct list *head = nullptr, *p, *q;
uint32_t op[300] = {0}, *pc = op, IR;
uint16_t GR[8];
int pos = 0;
char inout = 0;
bool out = false, in = false;
uint8_t ss[100] = {0}, x = 0;
uint8_t data[240] = {0};
int (*ops[])(void) = {HLT,   JMP,   CJMP,   OJMP,   CALL,  RET, PUSH, POP,
                      LOADB, LOADW, STOREB, STOREW, LOADI, NOP, IN,   OUT,
                      ADD,   ADDI,  SUB,    SUBI,   MUL,   DIV, AND,  OR,
                      NOR,   NOTB,  SAL,    SAR,    EQU,   LT,  LTE,  NOTC};

int HLT(void)
{
  pos++;
  return 0;
}
int JMP(void)
{
  pos = ADDRESS / 4;
  pc = op + ADDRESS / 4;
  return 1;
}
int CJMP(void)
{
  if (state.compare)
  {
    pos = ADDRESS / 4;
    pc = op + ADDRESS / 4;
  }
  else
  {
    pos++;
  }
  return 1;
}
int OJMP(void)
{
  if (state.overflow)
  {
    pos = ADDRESS / 4;
    pc = op + ADDRESS / 4;
  }
  else
  {
    pos++;
  }

  return 1;
}
int CALL(void)
{
  int i;
  p = head;
  if (head == nullptr)
  {
    p = reinterpret_cast<list *>(malloc(sizeof(struct list)));
    p->pt = pos;
    for (i = 0; i < 8; i++)
      p->G[i] = GR[i];
    p->s = state;
    p->next = nullptr;
    head = p;
  }
  else
  {
    for (; p != nullptr; p = p->next)
      q = p;
    p = reinterpret_cast<list *>(malloc(sizeof(struct list)));
    p->pt = pos;

    for (i = 0; i < 8; i++)
      p->G[i] = GR[i];
    p->s = state;
    p->next = nullptr;
    q->next = p;
  }
  pos = ADDRESS / 4;
  pc = op + ADDRESS / 4;
  return 1;
}
int RET(void)
{
  int i;
  p = head;
  q = p;
  for (; p->next != nullptr; p = p->next)
    q = p;
  pc = op + p->pt + 1;
  pos = p->pt + 1;
  state = p->s;
  for (i = 0; i < 8; i++)
    GR[i] = p->G[i];
  q->next = nullptr;
  return 1;
}
int PUSH(void)
{
  if (x >= 100)
  {
    throw "ERROR:too many numbers\n";
  }
  if (REG0 == 0)
  {
    throw "ERROR:wrong register\n";
  }
  x++;
  ss[x] = GR[REG0];
  pos++;
  return 1;
}
int POP(void)
{
  if (x == 0)
  {
    throw "ERROR:too few numbers\n";
  }
  GR[REG0] = ss[x];
  x--;
  pos++;
  return 1;
}
int LOADB(void)
{
  GR[REG0] = data[GR[7]];
  pos++;
  return 1;
}
int LOADW(void)
{
  GR[REG0] = data[GR[7] + 1];
  GR[REG0] <<= 8;
  GR[REG0] += data[GR[7]];
  pos++;
  return 1;
}
int STOREB(void)
{
  data[GR[7]] = GR[REG0];
  pos++;
  return 1;
}
int STOREW(void)
{
  data[GR[7]] = GR[REG0] & 0xff;
  data[GR[7] + 1] = (GR[REG0] >> 8) & 0xff;
  pos++;
  return 1;
}
int LOADI(void)
{
  GR[REG0] = IMMEDIATE;
  pos++;
  return 1;
}
int NOP(void)
{
  pos++;
  return 1;
}
int IN(void)
{
  GR[REG0] = inout;
  pos++;
  return 1;
}
int OUT(void)
{
  inout = GR[REG0] & 0xff;
  pos++;
  out = true;
  return 1;
}
int ADD(void)
{
  long int a, b, c;
  b = GR[REG1];
  c = GR[REG2];
  GR[REG0] = GR[REG1] + GR[REG2];
  a = GR[REG0];
  if ((b + c) != a)
  {
    state.overflow = 1;
  }
  else
  {
    state.overflow = 0;
  }

  pos++;
  return 1;
}
int ADDI(void)
{
  int k;
  long int a, b, c;
  b = GR[REG0];
  c = IMMEDIATE;
  k = GR[REG0] + IMMEDIATE;
  a = k;
  if ((b + c) != a)
  {
    state.overflow = 1;
  }
  else
  {
    state.overflow = 0;
  }

  GR[REG0] = k;
  pos++;
  return 1;
}
int SUB(void)
{
  long int a, b, c;
  b = GR[REG1];
  c = GR[REG2];
  GR[REG0] = GR[REG1] - GR[REG2];
  a = GR[REG0];
  if ((b - c) != a)
  {
    state.overflow = 1;
  }
  else
  {
    state.overflow = 0;
  }
  pos++;
  return 1;
}
int SUBI(void)
{
  int k;
  long int a, b, c;
  b = GR[REG0];
  c = IMMEDIATE;
  k = GR[REG0] - IMMEDIATE;
  a = k;
  if ((b - c) != a)
  {
    state.overflow = 1;
  }
  else
  {
    state.overflow = 0;
  }

  GR[REG0] = k;
  pos++;
  return 1;
}
int MUL(void)
{
  long int a, b, c;
  b = GR[REG1];
  c = GR[REG2];
  GR[REG0] = GR[REG1] * GR[REG2];
  a = GR[REG0];
  if ((b * c) != a)
  {
    state.overflow = 1;
  }
  else
  {
    state.overflow = 0;
  }

  pos++;
  return 1;
}
int DIV(void)
{
  long int a, b, c;
  if (GR[REG2] == 0)
  {
    throw "WRONG NUMBER\n";
  }
  b = GR[REG1];
  c = GR[REG2];

  GR[REG0] = GR[REG1] / GR[REG2];
  a = GR[REG0];
  if ((b / c) != a)
  {
    state.overflow = 1;
  }
  else
  {
    state.overflow = 0;
  }

  pos++;
  return 1;
}
int AND(void)
{
  GR[REG0] = GR[REG1] & GR[REG2];
  pos++;
  return 1;
}
int OR(void)
{
  GR[REG0] = GR[REG1] | GR[REG2];
  pos++;
  return 1;
}
int NOR(void)
{
  GR[REG0] = GR[REG1] ^ GR[REG2];
  pos++;
  return 1;
}
int NOTB(void)
{
  GR[REG0] = ~GR[REG1];
  pos++;
  return 1;
}
int SAL(void)
{
  GR[REG0] = GR[REG1] << GR[REG2];
  pos++;
  return 1;
}
int SAR(void)
{
  GR[REG0] = GR[REG1] >> GR[REG2];
  pos++;
  return 1;
}
int EQU(void)
{
  if (GR[REG0] == GR[REG1])
  {
    state.compare = 1;
  }
  else
  {
    state.compare = 0;
  }

  pos++;
  return 1;
}
int LT(void)
{
  if (GR[REG0] < GR[REG1])
    state.compare = 1;
  else
    state.compare = 0;
  pos++;
  return 1;
}
int LTE(void)
{
  if (GR[REG0] <= GR[REG1])
    state.compare = 1;
  else
    state.compare = 0;
  pos++;
  return 1;
}
int NOTC(void)
{
  if (state.compare == 1)
  {
    state.compare = 0;
  }
  else
  {
    state.compare = 1;
  }

  pos++;
  return 1;
}
} // namespace simu_kernel
